
#include "buffers.h"

#include <vector>
#include <stack>
#include <boost/thread/mutex.hpp>

#include <iostream>

namespace dhd
{
   namespace
   {
      class HeapBuffer;
      
      typedef std::stack< HeapBuffer *, std::vector<HeapBuffer *> > BufferStack;
      
      boost::mutex g_buffer_mutex;
      
      bool g_buffers_deleted = false;
      
      
      class HeapBuffer : public Buffer
      {
        public:
          
          HeapBuffer( int size ) : Buffer( new char[size], size )
          {}
          
          ~HeapBuffer()
          {
              delete [] pbuff;
          }
          
          virtual void on_zero_refcount();
          
          friend void empty_stack( BufferStack * s );
          friend PBuffer dhd_allocate_buffer( int nbytes );
      };
      
      
      BufferStack g_k_1;
      BufferStack g_k_4;
      BufferStack g_k_8;
      BufferStack g_k_16;
      BufferStack g_k_32;
      BufferStack g_k_64;
      
      
      BufferStack * get_stack( int buffer_size )
      {
          if ( buffer_size <= 1 * 1024 )  return & g_k_1;    
          if ( buffer_size <= 4 * 1024 )  return & g_k_4;
          if ( buffer_size <= 8 * 1024 )  return & g_k_8;
          if ( buffer_size <= 16 * 1024 ) return & g_k_16;
          if ( buffer_size <= 32 * 1024 ) return & g_k_32;
          
          assert( buffer_size <= 64 * 1024 );
          
          return & g_k_64;
      }
      
      
      void empty_stack( BufferStack * s )
      {
          while ( ! s->empty() )
          {
              delete s->top();
              s->pop();
          }
      }
      
      
      void HeapBuffer::on_zero_refcount()
      {        
          boost::mutex::scoped_lock guard( g_buffer_mutex );
          
          if ( g_buffers_deleted )
              delete this; // process is shutting down
          else
              get_stack( max_size() )->push( this );
      }
      
   }// end anonymous namespace
   
   
   
   
   void delete_buffer_cache()
   {
       boost::mutex::scoped_lock( g_buffer_mutex );
       
       empty_stack( &g_k_1  );
       empty_stack( &g_k_4  );
       empty_stack( &g_k_8  );
       empty_stack( &g_k_16 );
       empty_stack( &g_k_32 );
       empty_stack( &g_k_64 );
       
       g_buffers_deleted = true;
   }
   
   
   PBuffer allocate_buffer( int nbytes )
   {
       assert( nbytes <= 64 * 1024 );
       
       boost::mutex::scoped_lock( g_buffer_mutex );
       
       HeapBuffer  * b = 0;
       BufferStack * s = get_stack( nbytes );
       
       if ( s->empty() )
       {
           if ( nbytes <= 1 * 1024 )        b = new HeapBuffer(  1 * 1024 );
           else if ( nbytes <=  4 * 1024 )  b = new HeapBuffer(  4 * 1024 );
           else if ( nbytes <=  8 * 1024 )  b = new HeapBuffer(  8 * 1024 );
           else if ( nbytes <= 16 * 1024 )  b = new HeapBuffer( 16 * 1024 );
           else if ( nbytes <= 32 * 1024 )  b = new HeapBuffer( 32 * 1024 );
           else                             b = new HeapBuffer( 64 * 1024 );
       }
       else
       {
           b = s->top();
           s->pop();
       }
       
       b->set_size( nbytes );
       
       return PBuffer( b );
   }
   
} // end namespace dhd

